// SPDX-License-Identifier: GPL-2.0
/*
 * Support for Intel Camera Imaging ISP subsystem.
 * Copyright (c) 2015, Intel Corporation.
 */

#include "hmm.h"

#include "sh_css_sp.h"

#include "input_formatter.h"

#include "dma.h"	/* N_DMA_CHANNEL_ID */

#include "ia_css_buffer.h"
#include "ia_css_binary.h"
#include "sh_css_hrt.h"
#include "sh_css_defs.h"
#include "sh_css_internal.h"
#include "ia_css_control.h"
#include "ia_css_debug.h"
#include "ia_css_debug_pipe.h"
#include "ia_css_event_public.h"
#include "ia_css_mmu.h"
#include "ia_css_stream.h"
#include "ia_css_isp_param.h"
#include "sh_css_params.h"
#include "sh_css_legacy.h"
#include "ia_css_frame_comm.h"
#include "ia_css_isys.h"

#include "gdc_device.h"				/* HRT_GDC_N */

/*#include "sp.h"*/	/* host2sp_enqueue_frame_data() */


#include "assert_support.h"

#include "sw_event_global.h"			/* Event IDs.*/
#include "ia_css_event.h"
#include "mmu_device.h"
#include "ia_css_spctrl.h"
#include "atomisp_internal.h"

#ifndef offsetof
#define offsetof(T, x) ((unsigned int)&(((T *)0)->x))
#endif

#define IA_CSS_INCLUDE_CONFIGURATIONS
#include "ia_css_isp_configs.h"
#define IA_CSS_INCLUDE_STATES
#include "ia_css_isp_states.h"

#include "isp/kernels/ipu2_io_ls/bayer_io_ls/ia_css_bayer_io.host.h"

struct sh_css_sp_group		sh_css_sp_group;
struct sh_css_sp_stage		sh_css_sp_stage;
struct sh_css_isp_stage		sh_css_isp_stage;
static struct sh_css_sp_output		sh_css_sp_output;
static struct sh_css_sp_per_frame_data per_frame_data;

/* true if SP supports frame loop and host2sp_commands */
/* For the moment there is only code that sets this bool to true */
/* TODO: add code that sets this bool to false */
static bool sp_running;

static int
set_output_frame_buffer(const struct ia_css_frame *frame,
			unsigned int idx);

static void
sh_css_copy_buffer_attr_to_spbuffer(struct ia_css_buffer_sp *dest_buf,
				    const enum sh_css_queue_id queue_id,
				    const ia_css_ptr xmem_addr,
				    const enum ia_css_buffer_type buf_type);

static void
initialize_frame_buffer_attribute(struct ia_css_buffer_sp *buf_attr);

static void
initialize_stage_frames(struct ia_css_frames_sp *frames);

/* This data is stored every frame */
void
store_sp_group_data(void)
{
	per_frame_data.sp_group_addr = sh_css_store_sp_group_to_ddr();
}

static void
copy_isp_stage_to_sp_stage(void)
{
	/* [WW07.5]type casting will cause potential issues */
	sh_css_sp_stage.num_stripes = (uint8_t)
				      sh_css_isp_stage.binary_info.iterator.num_stripes;
	sh_css_sp_stage.row_stripes_height = (uint16_t)
					     sh_css_isp_stage.binary_info.iterator.row_stripes_height;
	sh_css_sp_stage.row_stripes_overlap_lines = (uint16_t)
		sh_css_isp_stage.binary_info.iterator.row_stripes_overlap_lines;
	sh_css_sp_stage.top_cropping = (uint16_t)
				       sh_css_isp_stage.binary_info.pipeline.top_cropping;
	sh_css_sp_stage.enable.sdis = sh_css_isp_stage.binary_info.enable.dis;
	sh_css_sp_stage.enable.s3a = sh_css_isp_stage.binary_info.enable.s3a;
}

void
store_sp_stage_data(enum ia_css_pipe_id id, unsigned int pipe_num,
		    unsigned int stage)
{
	unsigned int thread_id;

	ia_css_pipeline_get_sp_thread_id(pipe_num, &thread_id);
	copy_isp_stage_to_sp_stage();
	if (id != IA_CSS_PIPE_ID_COPY)
		sh_css_sp_stage.isp_stage_addr =
		    sh_css_store_isp_stage_to_ddr(pipe_num, stage);
	sh_css_sp_group.pipe[thread_id].sp_stage_addr[stage] =
	    sh_css_store_sp_stage_to_ddr(pipe_num, stage);

	/* Clear for next frame */
	sh_css_sp_stage.program_input_circuit = false;
}

static void
store_sp_per_frame_data(const struct ia_css_fw_info *fw)
{
	unsigned int HIVE_ADDR_sp_per_frame_data = 0;

	assert(fw);

	switch (fw->type) {
	case ia_css_sp_firmware:
		HIVE_ADDR_sp_per_frame_data = fw->info.sp.per_frame_data;
		break;
	case ia_css_acc_firmware:
		HIVE_ADDR_sp_per_frame_data = fw->info.acc.per_frame_data;
		break;
	case ia_css_isp_firmware:
		return;
	}

	sp_dmem_store(SP0_ID,
		      (unsigned int)sp_address_of(sp_per_frame_data),
		      &per_frame_data,
		      sizeof(per_frame_data));
}

static void
sh_css_store_sp_per_frame_data(enum ia_css_pipe_id pipe_id,
			       unsigned int pipe_num,
			       const struct ia_css_fw_info *sp_fw)
{
	if (!sp_fw)
		sp_fw = &sh_css_sp_fw;

	store_sp_stage_data(pipe_id, pipe_num, 0);
	store_sp_group_data();
	store_sp_per_frame_data(sp_fw);
}

#if SP_DEBUG != SP_DEBUG_NONE

void
sh_css_sp_get_debug_state(struct sh_css_sp_debug_state *state)
{
	const struct ia_css_fw_info *fw = &sh_css_sp_fw;
	unsigned int HIVE_ADDR_sp_output = fw->info.sp.output;
	unsigned int i;
	unsigned int offset = (unsigned int)offsetof(struct sh_css_sp_output,
			      debug) / sizeof(int);

	assert(state);

	(void)HIVE_ADDR_sp_output; /* To get rid of warning in CRUN */
	for (i = 0; i < sizeof(*state) / sizeof(int); i++)
		((unsigned int *)state)[i] = load_sp_array_uint(sp_output, i + offset);
}

#endif

void
sh_css_sp_start_binary_copy(unsigned int pipe_num,
			    struct ia_css_frame *out_frame,
			    unsigned int two_ppc)
{
	enum ia_css_pipe_id pipe_id;
	unsigned int thread_id;
	struct sh_css_sp_pipeline *pipe;
	u8 stage_num = 0;

	assert(out_frame);
	pipe_id = IA_CSS_PIPE_ID_CAPTURE;
	ia_css_pipeline_get_sp_thread_id(pipe_num, &thread_id);
	pipe = &sh_css_sp_group.pipe[thread_id];

	pipe->copy.bin.bytes_available = out_frame->data_bytes;
	pipe->num_stages = 1;
	pipe->pipe_id = pipe_id;
	pipe->pipe_num = pipe_num;
	pipe->thread_id = thread_id;
	pipe->pipe_config = 0x0; /* No parameters */
	pipe->pipe_qos_config = QOS_INVALID;

	if (pipe->inout_port_config == 0) {
		SH_CSS_PIPE_PORT_CONFIG_SET(pipe->inout_port_config,
					    (uint8_t)SH_CSS_PORT_INPUT,
					    (uint8_t)SH_CSS_HOST_TYPE, 1);
		SH_CSS_PIPE_PORT_CONFIG_SET(pipe->inout_port_config,
					    (uint8_t)SH_CSS_PORT_OUTPUT,
					    (uint8_t)SH_CSS_HOST_TYPE, 1);
	}
	IA_CSS_LOG("pipe_id %d port_config %08x",
		   pipe->pipe_id, pipe->inout_port_config);

	if (!IS_ISP2401)
		sh_css_sp_group.config.input_formatter.isp_2ppc = (uint8_t)two_ppc;

	sh_css_sp_stage.num = stage_num;
	sh_css_sp_stage.stage_type = SH_CSS_SP_STAGE_TYPE;
	sh_css_sp_stage.func =
	    (unsigned int)IA_CSS_PIPELINE_BIN_COPY;

	set_output_frame_buffer(out_frame, 0);

	/* sp_bin_copy_init on the SP does not deal with dynamica/static yet */
	/* For now always update the dynamic data from out frames. */
	sh_css_store_sp_per_frame_data(pipe_id, pipe_num, &sh_css_sp_fw);
}

static void
sh_css_sp_start_raw_copy(struct ia_css_frame *out_frame,
			 unsigned int pipe_num,
			 unsigned int two_ppc,
			 unsigned int max_input_width,
			 enum sh_css_pipe_config_override pipe_conf_override,
			 unsigned int if_config_index)
{
	enum ia_css_pipe_id pipe_id;
	unsigned int thread_id;
	u8 stage_num = 0;
	struct sh_css_sp_pipeline *pipe;

	assert(out_frame);

	{
		/*
		 * Clear sh_css_sp_stage for easy debugging.
		 * program_input_circuit must be saved as it is set outside
		 * this function.
		 */
		u8 program_input_circuit;

		program_input_circuit = sh_css_sp_stage.program_input_circuit;
		memset(&sh_css_sp_stage, 0, sizeof(sh_css_sp_stage));
		sh_css_sp_stage.program_input_circuit = program_input_circuit;
	}

	pipe_id = IA_CSS_PIPE_ID_COPY;
	ia_css_pipeline_get_sp_thread_id(pipe_num, &thread_id);
	pipe = &sh_css_sp_group.pipe[thread_id];

	pipe->copy.raw.height	    = out_frame->frame_info.res.height;
	pipe->copy.raw.width	    = out_frame->frame_info.res.width;
	pipe->copy.raw.padded_width  = out_frame->frame_info.padded_width;
	pipe->copy.raw.raw_bit_depth = out_frame->frame_info.raw_bit_depth;
	pipe->copy.raw.max_input_width = max_input_width;
	pipe->num_stages = 1;
	pipe->pipe_id = pipe_id;
	/* TODO: next indicates from which queues parameters need to be
		 sampled, needs checking/improvement */
	if (pipe_conf_override == SH_CSS_PIPE_CONFIG_OVRD_NO_OVRD)
		pipe->pipe_config =
		    (SH_CSS_PIPE_CONFIG_SAMPLE_PARAMS << thread_id);
	else
		pipe->pipe_config = pipe_conf_override;

	pipe->pipe_qos_config = QOS_INVALID;

	if (pipe->inout_port_config == 0) {
		SH_CSS_PIPE_PORT_CONFIG_SET(pipe->inout_port_config,
					    (uint8_t)SH_CSS_PORT_INPUT,
					    (uint8_t)SH_CSS_HOST_TYPE, 1);
		SH_CSS_PIPE_PORT_CONFIG_SET(pipe->inout_port_config,
					    (uint8_t)SH_CSS_PORT_OUTPUT,
					    (uint8_t)SH_CSS_HOST_TYPE, 1);
	}
	IA_CSS_LOG("pipe_id %d port_config %08x",
		   pipe->pipe_id, pipe->inout_port_config);

	if (!IS_ISP2401)
		sh_css_sp_group.config.input_formatter.isp_2ppc = (uint8_t)two_ppc;

	sh_css_sp_stage.num = stage_num;
	sh_css_sp_stage.xmem_bin_addr = 0x0;
	sh_css_sp_stage.stage_type = SH_CSS_SP_STAGE_TYPE;
	sh_css_sp_stage.func = (unsigned int)IA_CSS_PIPELINE_RAW_COPY;
	sh_css_sp_stage.if_config_index = (uint8_t)if_config_index;
	set_output_frame_buffer(out_frame, 0);

	ia_css_debug_pipe_graph_dump_sp_raw_copy(out_frame);
}

static void
sh_css_sp_start_isys_copy(struct ia_css_frame *out_frame,
			  unsigned int pipe_num, unsigned int max_input_width,
			  unsigned int if_config_index)
{
	enum ia_css_pipe_id pipe_id;
	unsigned int thread_id;
	u8 stage_num = 0;
	struct sh_css_sp_pipeline *pipe;
	enum sh_css_queue_id queue_id;

	assert(out_frame);

	{
		/*
		 * Clear sh_css_sp_stage for easy debugging.
		 * program_input_circuit must be saved as it is set outside
		 * this function.
		 */
		u8 program_input_circuit;

		program_input_circuit = sh_css_sp_stage.program_input_circuit;
		memset(&sh_css_sp_stage, 0, sizeof(sh_css_sp_stage));
		sh_css_sp_stage.program_input_circuit = program_input_circuit;
	}

	pipe_id = IA_CSS_PIPE_ID_COPY;
	ia_css_pipeline_get_sp_thread_id(pipe_num, &thread_id);
	pipe = &sh_css_sp_group.pipe[thread_id];

	pipe->copy.raw.height		= out_frame->frame_info.res.height;
	pipe->copy.raw.width		= out_frame->frame_info.res.width;
	pipe->copy.raw.padded_width	= out_frame->frame_info.padded_width;
	pipe->copy.raw.raw_bit_depth	= out_frame->frame_info.raw_bit_depth;
	pipe->copy.raw.max_input_width	= max_input_width;
	pipe->num_stages		= 1;
	pipe->pipe_id			= pipe_id;
	pipe->pipe_config		= 0x0;	/* No parameters */
	pipe->pipe_qos_config		= QOS_INVALID;

	initialize_stage_frames(&sh_css_sp_stage.frames);
	sh_css_sp_stage.num = stage_num;
	sh_css_sp_stage.xmem_bin_addr = 0x0;
	sh_css_sp_stage.stage_type = SH_CSS_SP_STAGE_TYPE;
	sh_css_sp_stage.func = (unsigned int)IA_CSS_PIPELINE_ISYS_COPY;
	sh_css_sp_stage.if_config_index = (uint8_t)if_config_index;

	set_output_frame_buffer(out_frame, 0);

	if (pipe->metadata.height > 0) {
		ia_css_query_internal_queue_id(IA_CSS_BUFFER_TYPE_METADATA, thread_id,
					       &queue_id);
		sh_css_copy_buffer_attr_to_spbuffer(&sh_css_sp_stage.frames.metadata_buf,
						    queue_id, mmgr_EXCEPTION,
						    IA_CSS_BUFFER_TYPE_METADATA);
	}

	ia_css_debug_pipe_graph_dump_sp_raw_copy(out_frame);
}

unsigned int
sh_css_sp_get_binary_copy_size(void)
{
	const struct ia_css_fw_info *fw = &sh_css_sp_fw;
	unsigned int HIVE_ADDR_sp_output = fw->info.sp.output;
	unsigned int offset = (unsigned int)offsetof(struct sh_css_sp_output,
			      bin_copy_bytes_copied) / sizeof(int);
	(void)HIVE_ADDR_sp_output; /* To get rid of warning in CRUN */
	return load_sp_array_uint(sp_output, offset);
}

unsigned int
sh_css_sp_get_sw_interrupt_value(unsigned int irq)
{
	const struct ia_css_fw_info *fw = &sh_css_sp_fw;
	unsigned int HIVE_ADDR_sp_output = fw->info.sp.output;
	unsigned int offset = (unsigned int)offsetof(struct sh_css_sp_output,
			      sw_interrupt_value)
			      / sizeof(int);
	(void)HIVE_ADDR_sp_output; /* To get rid of warning in CRUN */
	return load_sp_array_uint(sp_output, offset + irq);
}

static void
sh_css_copy_buffer_attr_to_spbuffer(struct ia_css_buffer_sp *dest_buf,
				    const enum sh_css_queue_id queue_id,
				    const ia_css_ptr xmem_addr,
				    const enum ia_css_buffer_type buf_type)
{
	assert(buf_type < IA_CSS_NUM_BUFFER_TYPE);
	if (queue_id > SH_CSS_INVALID_QUEUE_ID) {
		/*
		 * value >=0 indicates that function init_frame_pointers()
		 * should use the dynamic data address
		 */
		assert(queue_id < SH_CSS_MAX_NUM_QUEUES);

		/*
		 * Klocwork assumes assert can be disabled;
		 * Since we can get there with any type, and it does not
		 * know that frame_in->dynamic_data_index can only be set
		 * for one of the types in the assert) it has to assume we
		 * can get here for any type. however this could lead to an
		 * out of bounds reference when indexing buf_type about 10
		 * lines below. In order to satisfy KW an additional if
		 * has been added. This one will always yield true.
		 */
		if (queue_id < SH_CSS_MAX_NUM_QUEUES)
			dest_buf->buf_src.queue_id = queue_id;
	} else {
		assert(xmem_addr != mmgr_EXCEPTION);
		dest_buf->buf_src.xmem_addr = xmem_addr;
	}
	dest_buf->buf_type = buf_type;
}

static void
sh_css_copy_frame_to_spframe(struct ia_css_frame_sp *sp_frame_out,
			     const struct ia_css_frame *frame_in)
{
	assert(frame_in);

	ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE_PRIVATE,
			    "sh_css_copy_frame_to_spframe():\n");

	sh_css_copy_buffer_attr_to_spbuffer(&sp_frame_out->buf_attr,
					    frame_in->dynamic_queue_id,
					    frame_in->data,
					    frame_in->buf_type);

	ia_css_frame_info_to_frame_sp_info(&sp_frame_out->info, &frame_in->frame_info);

	switch (frame_in->frame_info.format) {
	case IA_CSS_FRAME_FORMAT_RAW_PACKED:
	case IA_CSS_FRAME_FORMAT_RAW:
		sp_frame_out->planes.raw.offset = frame_in->planes.raw.offset;
		break;
	case IA_CSS_FRAME_FORMAT_RGB565:
	case IA_CSS_FRAME_FORMAT_RGBA888:
		sp_frame_out->planes.rgb.offset = frame_in->planes.rgb.offset;
		break;
	case IA_CSS_FRAME_FORMAT_PLANAR_RGB888:
		sp_frame_out->planes.planar_rgb.r.offset =
		    frame_in->planes.planar_rgb.r.offset;
		sp_frame_out->planes.planar_rgb.g.offset =
		    frame_in->planes.planar_rgb.g.offset;
		sp_frame_out->planes.planar_rgb.b.offset =
		    frame_in->planes.planar_rgb.b.offset;
		break;
	case IA_CSS_FRAME_FORMAT_YUYV:
	case IA_CSS_FRAME_FORMAT_UYVY:
	case IA_CSS_FRAME_FORMAT_CSI_MIPI_YUV420_8:
	case IA_CSS_FRAME_FORMAT_CSI_MIPI_LEGACY_YUV420_8:
	case IA_CSS_FRAME_FORMAT_YUV_LINE:
		sp_frame_out->planes.yuyv.offset = frame_in->planes.yuyv.offset;
		break;
	case IA_CSS_FRAME_FORMAT_NV11:
	case IA_CSS_FRAME_FORMAT_NV12:
	case IA_CSS_FRAME_FORMAT_NV12_16:
	case IA_CSS_FRAME_FORMAT_NV12_TILEY:
	case IA_CSS_FRAME_FORMAT_NV21:
	case IA_CSS_FRAME_FORMAT_NV16:
	case IA_CSS_FRAME_FORMAT_NV61:
		sp_frame_out->planes.nv.y.offset =
		    frame_in->planes.nv.y.offset;
		sp_frame_out->planes.nv.uv.offset =
		    frame_in->planes.nv.uv.offset;
		break;
	case IA_CSS_FRAME_FORMAT_YUV420:
	case IA_CSS_FRAME_FORMAT_YUV422:
	case IA_CSS_FRAME_FORMAT_YUV444:
	case IA_CSS_FRAME_FORMAT_YUV420_16:
	case IA_CSS_FRAME_FORMAT_YUV422_16:
	case IA_CSS_FRAME_FORMAT_YV12:
	case IA_CSS_FRAME_FORMAT_YV16:
		sp_frame_out->planes.yuv.y.offset =
		    frame_in->planes.yuv.y.offset;
		sp_frame_out->planes.yuv.u.offset =
		    frame_in->planes.yuv.u.offset;
		sp_frame_out->planes.yuv.v.offset =
		    frame_in->planes.yuv.v.offset;
		break;
	case IA_CSS_FRAME_FORMAT_QPLANE6:
		sp_frame_out->planes.plane6.r.offset =
		    frame_in->planes.plane6.r.offset;
		sp_frame_out->planes.plane6.r_at_b.offset =
		    frame_in->planes.plane6.r_at_b.offset;
		sp_frame_out->planes.plane6.gr.offset =
		    frame_in->planes.plane6.gr.offset;
		sp_frame_out->planes.plane6.gb.offset =
		    frame_in->planes.plane6.gb.offset;
		sp_frame_out->planes.plane6.b.offset =
		    frame_in->planes.plane6.b.offset;
		sp_frame_out->planes.plane6.b_at_r.offset =
		    frame_in->planes.plane6.b_at_r.offset;
		break;
	case IA_CSS_FRAME_FORMAT_BINARY_8:
		sp_frame_out->planes.binary.data.offset =
		    frame_in->planes.binary.data.offset;
		break;
	default:
		/*
		 * This should not happen, but in case it does,
		 * nullify the planes
		 */
		memset(&sp_frame_out->planes, 0, sizeof(sp_frame_out->planes));
		break;
	}
}

static int
set_input_frame_buffer(const struct ia_css_frame *frame)
{
	if (!frame)
		return -EINVAL;

	switch (frame->frame_info.format) {
	case IA_CSS_FRAME_FORMAT_QPLANE6:
	case IA_CSS_FRAME_FORMAT_YUV420_16:
	case IA_CSS_FRAME_FORMAT_RAW_PACKED:
	case IA_CSS_FRAME_FORMAT_RAW:
	case IA_CSS_FRAME_FORMAT_YUV420:
	case IA_CSS_FRAME_FORMAT_YUYV:
	case IA_CSS_FRAME_FORMAT_YUV_LINE:
	case IA_CSS_FRAME_FORMAT_NV12:
	case IA_CSS_FRAME_FORMAT_NV12_16:
	case IA_CSS_FRAME_FORMAT_NV12_TILEY:
	case IA_CSS_FRAME_FORMAT_NV21:
	case IA_CSS_FRAME_FORMAT_CSI_MIPI_YUV420_8:
	case IA_CSS_FRAME_FORMAT_CSI_MIPI_LEGACY_YUV420_8:
	case IA_CSS_FRAME_FORMAT_CSI_MIPI_YUV420_10:
		break;
	default:
		return -EINVAL;
	}
	sh_css_copy_frame_to_spframe(&sh_css_sp_stage.frames.in, frame);

	return 0;
}

static int
set_output_frame_buffer(const struct ia_css_frame *frame,
			unsigned int idx)
{
	if (!frame)
		return -EINVAL;

	switch (frame->frame_info.format) {
	case IA_CSS_FRAME_FORMAT_YUV420:
	case IA_CSS_FRAME_FORMAT_YUV422:
	case IA_CSS_FRAME_FORMAT_YUV444:
	case IA_CSS_FRAME_FORMAT_YV12:
	case IA_CSS_FRAME_FORMAT_YV16:
	case IA_CSS_FRAME_FORMAT_YUV420_16:
	case IA_CSS_FRAME_FORMAT_YUV422_16:
	case IA_CSS_FRAME_FORMAT_NV11:
	case IA_CSS_FRAME_FORMAT_NV12:
	case IA_CSS_FRAME_FORMAT_NV12_16:
	case IA_CSS_FRAME_FORMAT_NV12_TILEY:
	case IA_CSS_FRAME_FORMAT_NV16:
	case IA_CSS_FRAME_FORMAT_NV21:
	case IA_CSS_FRAME_FORMAT_NV61:
	case IA_CSS_FRAME_FORMAT_YUYV:
	case IA_CSS_FRAME_FORMAT_UYVY:
	case IA_CSS_FRAME_FORMAT_CSI_MIPI_YUV420_8:
	case IA_CSS_FRAME_FORMAT_CSI_MIPI_LEGACY_YUV420_8:
	case IA_CSS_FRAME_FORMAT_YUV_LINE:
	case IA_CSS_FRAME_FORMAT_RGB565:
	case IA_CSS_FRAME_FORMAT_RGBA888:
	case IA_CSS_FRAME_FORMAT_PLANAR_RGB888:
	case IA_CSS_FRAME_FORMAT_RAW:
	case IA_CSS_FRAME_FORMAT_RAW_PACKED:
	case IA_CSS_FRAME_FORMAT_QPLANE6:
	case IA_CSS_FRAME_FORMAT_BINARY_8:
		break;
	default:
		return -EINVAL;
	}
	sh_css_copy_frame_to_spframe(&sh_css_sp_stage.frames.out[idx], frame);
	return 0;
}

static int
set_view_finder_buffer(const struct ia_css_frame *frame)
{
	if (!frame)
		return -EINVAL;

	switch (frame->frame_info.format) {
	/* the dual output pin */
	case IA_CSS_FRAME_FORMAT_NV12:
	case IA_CSS_FRAME_FORMAT_NV12_16:
	case IA_CSS_FRAME_FORMAT_NV21:
	case IA_CSS_FRAME_FORMAT_YUYV:
	case IA_CSS_FRAME_FORMAT_UYVY:
	case IA_CSS_FRAME_FORMAT_CSI_MIPI_YUV420_8:
	case IA_CSS_FRAME_FORMAT_CSI_MIPI_LEGACY_YUV420_8:
	case IA_CSS_FRAME_FORMAT_YUV420:
	case IA_CSS_FRAME_FORMAT_YV12:
	case IA_CSS_FRAME_FORMAT_NV12_TILEY:

	/* for vf_veceven */
	case IA_CSS_FRAME_FORMAT_YUV_LINE:
		break;
	default:
		return -EINVAL;
	}

	sh_css_copy_frame_to_spframe(&sh_css_sp_stage.frames.out_vf, frame);
	return 0;
}

void sh_css_sp_set_if_configs(
    const input_formatter_cfg_t	*config_a,
    const input_formatter_cfg_t	*config_b,
    const uint8_t		if_config_index
)
{
	assert(if_config_index < SH_CSS_MAX_IF_CONFIGS);
	assert(config_a);

	sh_css_sp_group.config.input_formatter.set[if_config_index].config_a =
	    *config_a;
	sh_css_sp_group.config.input_formatter.a_changed = true;

	if (config_b) {
		sh_css_sp_group.config.input_formatter.set[if_config_index].config_b =
		    *config_b;
		sh_css_sp_group.config.input_formatter.b_changed = true;
	}
}

void
sh_css_sp_program_input_circuit(int fmt_type,
				int ch_id,
				enum ia_css_input_mode input_mode)
{
	sh_css_sp_group.config.input_circuit.no_side_band = false;
	sh_css_sp_group.config.input_circuit.fmt_type     = fmt_type;
	sh_css_sp_group.config.input_circuit.ch_id	      = ch_id;
	sh_css_sp_group.config.input_circuit.input_mode   = input_mode;
	/*
	 * The SP group is only loaded at SP boot time and is read once
	 * change flags as "input_circuit_cfg_changed" must be reset on the SP
	 */
	sh_css_sp_group.config.input_circuit_cfg_changed = true;
	sh_css_sp_stage.program_input_circuit = true;
}

void
sh_css_sp_configure_sync_gen(int width, int height,
			     int hblank_cycles,
			     int vblank_cycles)
{
	sh_css_sp_group.config.sync_gen.width	       = width;
	sh_css_sp_group.config.sync_gen.height	       = height;
	sh_css_sp_group.config.sync_gen.hblank_cycles = hblank_cycles;
	sh_css_sp_group.config.sync_gen.vblank_cycles = vblank_cycles;
}

void
sh_css_sp_configure_prbs(int seed)
{
	sh_css_sp_group.config.prbs.seed = seed;
}

void
sh_css_sp_configure_enable_raw_pool_locking(bool lock_all)
{
	sh_css_sp_group.config.enable_raw_pool_locking = true;
	sh_css_sp_group.config.lock_all = lock_all;
}

void
sh_css_sp_enable_isys_event_queue(bool enable)
{
	sh_css_sp_group.config.enable_isys_event_queue = enable;
}

void
sh_css_sp_set_disable_continuous_viewfinder(bool flag)
{
	sh_css_sp_group.config.disable_cont_vf = flag;
}

static int
sh_css_sp_write_frame_pointers(const struct sh_css_binary_args *args)
{
	int err = 0;
	int i;

	assert(args);

	if (args->in_frame)
		err = set_input_frame_buffer(args->in_frame);
	if (!err && args->out_vf_frame)
		err = set_view_finder_buffer(args->out_vf_frame);
	for (i = 0; i < IA_CSS_BINARY_MAX_OUTPUT_PORTS; i++) {
		if (!err && args->out_frame[i])
			err = set_output_frame_buffer(args->out_frame[i], i);
	}

	/* we don't pass this error back to the upper layer, so we add a assert here
	   because we actually hit the error here but it still works by accident... */
	if (err)
		assert(false);
	return err;
}

static void
sh_css_sp_init_group(bool two_ppc,
		     enum atomisp_input_format input_format,
		     bool no_isp_sync,
		     uint8_t if_config_index)
{
	if (!IS_ISP2401)
		sh_css_sp_group.config.input_formatter.isp_2ppc = two_ppc;

	sh_css_sp_group.config.no_isp_sync = (uint8_t)no_isp_sync;
	/* decide whether the frame is processed online or offline */
	if (if_config_index == SH_CSS_IF_CONFIG_NOT_NEEDED)
		return;

	if (!IS_ISP2401) {
		assert(if_config_index < SH_CSS_MAX_IF_CONFIGS);
		sh_css_sp_group.config.input_formatter.set[if_config_index].stream_format =
		    input_format;
	}
}

void
sh_css_stage_write_binary_info(struct ia_css_binary_info *info)
{
	assert(info);
	sh_css_isp_stage.binary_info = *info;
}

static int
copy_isp_mem_if_to_ddr(struct ia_css_binary *binary)
{
	int err;

	err = ia_css_isp_param_copy_isp_mem_if_to_ddr(
	    &binary->css_params,
	    &binary->mem_params,
	    IA_CSS_PARAM_CLASS_CONFIG);
	if (err)
		return err;
	err = ia_css_isp_param_copy_isp_mem_if_to_ddr(
	    &binary->css_params,
	    &binary->mem_params,
	    IA_CSS_PARAM_CLASS_STATE);
	if (err)
		return err;
	return 0;
}

static bool
is_sp_stage(struct ia_css_pipeline_stage *stage)
{
	assert(stage);
	return stage->sp_func != IA_CSS_PIPELINE_NO_FUNC;
}

static int configure_isp_from_args(const struct sh_css_sp_pipeline *pipeline,
				   const struct ia_css_binary      *binary,
				   const struct sh_css_binary_args *args,
				   bool				   two_ppc,
				   bool				   deinterleaved)
{
	int ret;

	ret = ia_css_fpn_configure(binary,  &binary->in_frame_info);
	if (ret)
		return ret;
	ret = ia_css_crop_configure(binary, ia_css_frame_get_info(args->delay_frames[0]));
	if (ret)
		return ret;
	ret = ia_css_qplane_configure(pipeline, binary, &binary->in_frame_info);
	if (ret)
		return ret;
	ret = ia_css_output0_configure(binary, ia_css_frame_get_info(args->out_frame[0]));
	if (ret)
		return ret;
	ret = ia_css_output1_configure(binary, ia_css_frame_get_info(args->out_vf_frame));
	if (ret)
		return ret;
	ret = ia_css_copy_output_configure(binary, args->copy_output);
	if (ret)
		return ret;
	ret = ia_css_output0_configure(binary, ia_css_frame_get_info(args->out_frame[0]));
	if (ret)
		return ret;
	ret = ia_css_iterator_configure(binary, ia_css_frame_get_info(args->in_frame));
	if (ret)
		return ret;
	ret = ia_css_dvs_configure(binary, ia_css_frame_get_info(args->out_frame[0]));
	if (ret)
		return ret;
	ret = ia_css_output_configure(binary, ia_css_frame_get_info(args->out_frame[0]));
	if (ret)
		return ret;
	ret = ia_css_raw_configure(pipeline, binary, ia_css_frame_get_info(args->in_frame),
				   &binary->in_frame_info, two_ppc, deinterleaved);
	if (ret)
		return ret;

	/*
	 * FIXME: args->delay_frames can be NULL here
	 *
	 * Somehow, the driver at the Intel Atom Yocto tree doesn't seem to
	 * suffer from the same issue.
	 *
	 * Anyway, the function below should now handle a NULL delay_frames
	 * without crashing, but the pipeline should likely be built without
	 * adding it at the first place (or there are a hidden bug somewhere)
	 */
	ret = ia_css_ref_configure(binary, args->delay_frames, pipeline->dvs_frame_delay);
	if (ret)
		return ret;
	ret = ia_css_tnr_configure(binary, args->tnr_frames);
	if (ret)
		return ret;
	return ia_css_bayer_io_config(binary, args);
}

static void
initialize_isp_states(const struct ia_css_binary *binary)
{
	unsigned int i;

	if (!binary->info->mem_offsets.offsets.state)
		return;

	for (i = 0; i < IA_CSS_NUM_STATE_IDS; i++)
		ia_css_kernel_init_state[i](binary);
}

static void
initialize_frame_buffer_attribute(struct ia_css_buffer_sp *buf_attr)
{
	buf_attr->buf_src.queue_id = SH_CSS_INVALID_QUEUE_ID;
	buf_attr->buf_type = IA_CSS_BUFFER_TYPE_INVALID;
}

static void
initialize_stage_frames(struct ia_css_frames_sp *frames)
{
	unsigned int i;

	initialize_frame_buffer_attribute(&frames->in.buf_attr);
	for (i = 0; i < IA_CSS_BINARY_MAX_OUTPUT_PORTS; i++)
		initialize_frame_buffer_attribute(&frames->out[i].buf_attr);

	initialize_frame_buffer_attribute(&frames->out_vf.buf_attr);
	initialize_frame_buffer_attribute(&frames->s3a_buf);
	initialize_frame_buffer_attribute(&frames->dvs_buf);
	initialize_frame_buffer_attribute(&frames->metadata_buf);
}

static int
sh_css_sp_init_stage(struct ia_css_binary *binary,
		     const char *binary_name,
		     const struct ia_css_blob_info *blob_info,
		     const struct sh_css_binary_args *args,
		     unsigned int pipe_num,
		     unsigned int stage,
		     bool xnr,
		     const struct ia_css_isp_param_css_segments *isp_mem_if,
		     unsigned int if_config_index,
		     bool two_ppc)
{
	const struct ia_css_binary_xinfo *xinfo;
	const struct ia_css_binary_info  *info;
	int err = 0;
	int i;
	struct ia_css_pipe *pipe = NULL;
	unsigned int thread_id;
	enum sh_css_queue_id queue_id;
	bool continuous = sh_css_continuous_is_enabled((uint8_t)pipe_num);

	assert(binary);
	assert(blob_info);
	assert(args);
	assert(isp_mem_if);

	xinfo = binary->info;
	info  = &xinfo->sp;
	{
		/*
		 * Clear sh_css_sp_stage for easy debugging.
		 * program_input_circuit must be saved as it is set outside
		 * this function.
		 */
		u8 program_input_circuit;

		program_input_circuit = sh_css_sp_stage.program_input_circuit;
		memset(&sh_css_sp_stage, 0, sizeof(sh_css_sp_stage));
		sh_css_sp_stage.program_input_circuit = (uint8_t)program_input_circuit;
	}

	ia_css_pipeline_get_sp_thread_id(pipe_num, &thread_id);

	if (!info) {
		sh_css_sp_group.pipe[thread_id].sp_stage_addr[stage] = mmgr_NULL;
		return 0;
	}

	if (IS_ISP2401)
		sh_css_sp_stage.deinterleaved = 0;
	else
		sh_css_sp_stage.deinterleaved = ((stage == 0) && continuous);

	initialize_stage_frames(&sh_css_sp_stage.frames);
	/*
	 * TODO: Make the Host dynamically determine
	 * the stage type.
	 */
	sh_css_sp_stage.stage_type = SH_CSS_ISP_STAGE_TYPE;
	sh_css_sp_stage.num		= (uint8_t)stage;
	sh_css_sp_stage.isp_online	= (uint8_t)binary->online;
	sh_css_sp_stage.isp_copy_vf     = (uint8_t)args->copy_vf;
	sh_css_sp_stage.isp_copy_output = (uint8_t)args->copy_output;
	sh_css_sp_stage.enable.vf_output = (args->out_vf_frame != NULL);

	/*
	 * Copy the frame infos first, to be overwritten by the frames,
	 * if these are present.
	 */
	sh_css_sp_stage.frames.effective_in_res.width = binary->effective_in_frame_res.width;
	sh_css_sp_stage.frames.effective_in_res.height = binary->effective_in_frame_res.height;

	ia_css_frame_info_to_frame_sp_info(&sh_css_sp_stage.frames.in.info,
					   &binary->in_frame_info);
	for (i = 0; i < IA_CSS_BINARY_MAX_OUTPUT_PORTS; i++) {
		ia_css_frame_info_to_frame_sp_info(&sh_css_sp_stage.frames.out[i].info,
						   &binary->out_frame_info[i]);
	}
	ia_css_frame_info_to_frame_sp_info(&sh_css_sp_stage.frames.internal_frame_info,
					   &binary->internal_frame_info);
	sh_css_sp_stage.dvs_envelope.width    = binary->dvs_envelope.width;
	sh_css_sp_stage.dvs_envelope.height   = binary->dvs_envelope.height;
	sh_css_sp_stage.isp_pipe_version      = (uint8_t)info->pipeline.isp_pipe_version;
	sh_css_sp_stage.isp_deci_log_factor   = (uint8_t)binary->deci_factor_log2;
	sh_css_sp_stage.isp_vf_downscale_bits = (uint8_t)binary->vf_downscale_log2;

	sh_css_sp_stage.if_config_index = (uint8_t)if_config_index;

	sh_css_sp_stage.sp_enable_xnr = (uint8_t)xnr;
	sh_css_sp_stage.xmem_bin_addr = xinfo->xmem_addr;
	sh_css_sp_stage.xmem_map_addr = sh_css_params_ddr_address_map();
	sh_css_isp_stage.blob_info = *blob_info;
	sh_css_stage_write_binary_info((struct ia_css_binary_info *)info);

	/* Make sure binary name is smaller than allowed string size */
	assert(strlen(binary_name) < SH_CSS_MAX_BINARY_NAME - 1);
	strscpy(sh_css_isp_stage.binary_name, binary_name, SH_CSS_MAX_BINARY_NAME);
	sh_css_isp_stage.mem_initializers = *isp_mem_if;

	/*
	 * Even when a stage does not need uds and does not params,
	 * ia_css_uds_sp_scale_params() seems to be called (needs
	 * further investigation). This function can not deal with
	 * dx, dy = {0, 0}
	 */

	err = sh_css_sp_write_frame_pointers(args);
	/* TODO: move it to a better place */
	if (binary->info->sp.enable.s3a) {
		ia_css_query_internal_queue_id(IA_CSS_BUFFER_TYPE_3A_STATISTICS, thread_id,
					       &queue_id);
		sh_css_copy_buffer_attr_to_spbuffer(&sh_css_sp_stage.frames.s3a_buf, queue_id,
						    mmgr_EXCEPTION,
						    IA_CSS_BUFFER_TYPE_3A_STATISTICS);
	}
	if (binary->info->sp.enable.dis) {
		ia_css_query_internal_queue_id(IA_CSS_BUFFER_TYPE_DIS_STATISTICS, thread_id,
					       &queue_id);
		sh_css_copy_buffer_attr_to_spbuffer(&sh_css_sp_stage.frames.dvs_buf, queue_id,
						    mmgr_EXCEPTION,
						    IA_CSS_BUFFER_TYPE_DIS_STATISTICS);
	}
	ia_css_query_internal_queue_id(IA_CSS_BUFFER_TYPE_METADATA, thread_id, &queue_id);
	sh_css_copy_buffer_attr_to_spbuffer(&sh_css_sp_stage.frames.metadata_buf, queue_id, mmgr_EXCEPTION, IA_CSS_BUFFER_TYPE_METADATA);
	if (err)
		return err;

	if (IS_ISP2401) {
		pipe = find_pipe_by_num(sh_css_sp_group.pipe[thread_id].pipe_num);
		if (!pipe)
			return -EINVAL;

		if (args->in_frame)
			ia_css_get_crop_offsets(pipe, &args->in_frame->frame_info);
		else
			ia_css_get_crop_offsets(pipe, &binary->in_frame_info);
	}

	err = configure_isp_from_args(&sh_css_sp_group.pipe[thread_id],
				      binary, args, two_ppc, sh_css_sp_stage.deinterleaved);
	if (err)
		return err;

	initialize_isp_states(binary);

	/*
	 * We do this only for preview pipe because in fill_binary_info function
	 * we assign vf_out res to out res, but for ISP internal processing, we need
	 * the original out res. for video pipe, it has two output pins --- out and
	 * vf_out, so it can keep these two resolutions already.
	 */
	if (binary->info->sp.pipeline.mode == IA_CSS_BINARY_MODE_PREVIEW &&
	    (binary->vf_downscale_log2 > 0)) {
		/*
		 * TODO: Remove this after preview output decimation is fixed
		 * by configuring out&vf info fields properly.
		 */
		sh_css_sp_stage.frames.out[0].info.padded_width
		<<= binary->vf_downscale_log2;
		sh_css_sp_stage.frames.out[0].info.res.width
		<<= binary->vf_downscale_log2;
		sh_css_sp_stage.frames.out[0].info.res.height
		<<= binary->vf_downscale_log2;
	}
	err = copy_isp_mem_if_to_ddr(binary);
	if (err)
		return err;

	return 0;
}

static int
sp_init_stage(struct ia_css_pipeline_stage *stage,
	      unsigned int pipe_num,
	      bool xnr,
	      unsigned int if_config_index,
	      bool two_ppc)
{
	struct ia_css_binary *binary;
	const struct ia_css_fw_info *firmware;
	const struct sh_css_binary_args *args;
	unsigned int stage_num;
	/*
	 * Initialiser required because of the "else" path below.
	 * Is this a valid path ?
	 */
	const char *binary_name = "";
	const struct ia_css_binary_xinfo *info = NULL;
	/*
	 * Note: the var below is made static as it is quite large;
	 * if it is not static it ends up on the stack which could
	 * cause issues for drivers
	 */
	static struct ia_css_binary tmp_binary;
	const struct ia_css_blob_info *blob_info = NULL;
	struct ia_css_isp_param_css_segments isp_mem_if;
	/*
	 * LA: should be ia_css_data, should not contain host pointer.
	 * However, CSS/DDR pointer is not available yet.
	 * Hack is to store it in params->ddr_ptrs and then copy it late in
	 * the SP just before vmem init.
	 * TODO: Call this after CSS/DDR allocation and store that pointer.
	 * Best is to allocate it at stage creation time together with host
	 * pointer. Remove vmem from params.
	 */
	struct ia_css_isp_param_css_segments *mem_if = &isp_mem_if;

	int err = 0;

	assert(stage);

	binary = stage->binary;
	firmware = stage->firmware;
	args = &stage->args;
	stage_num = stage->stage_num;

	if (binary) {
		info = binary->info;
		binary_name = (const char *)(info->blob->name);
		blob_info = &info->blob->header.blob;
		ia_css_init_memory_interface(mem_if, &binary->mem_params, &binary->css_params);
	} else if (firmware) {
		const struct ia_css_frame_info *out_infos[IA_CSS_BINARY_MAX_OUTPUT_PORTS] = {NULL};

		if (args->out_frame[0])
			out_infos[0] = &args->out_frame[0]->frame_info;
		info = &firmware->info.isp;
		ia_css_binary_fill_info(info, false, false,
					ATOMISP_INPUT_FORMAT_RAW_10,
					ia_css_frame_get_info(args->in_frame),
					NULL,
					out_infos,
					ia_css_frame_get_info(args->out_vf_frame),
					&tmp_binary,
					NULL,
					-1, true);
		binary = &tmp_binary;
		binary->info = info;
		binary_name = IA_CSS_EXT_ISP_PROG_NAME(firmware);
		blob_info = &firmware->blob;
		mem_if = (struct ia_css_isp_param_css_segments *)&firmware->mem_initializers;
	} else {
		/* SP stage */
		assert(stage->sp_func != IA_CSS_PIPELINE_NO_FUNC);
		/*
		 * binary and blob_info are now NULL.
		 * These will be passed to sh_css_sp_init_stage
		 * and dereferenced there, so passing a NULL
		 * pointer is no good. return an error
		 */
		return -EINVAL;
	}

	err = sh_css_sp_init_stage(binary,
				   (const char *)binary_name,
				   blob_info,
				   args,
				   pipe_num,
				   stage_num,
				   xnr,
				   mem_if,
				   if_config_index,
				   two_ppc);
	return err;
}

static void
sp_init_sp_stage(struct ia_css_pipeline_stage *stage,
		 unsigned int pipe_num,
		 bool two_ppc,
		 enum sh_css_pipe_config_override copy_ovrd,
		 unsigned int if_config_index)
{
	const struct sh_css_binary_args *args = &stage->args;

	assert(stage);
	switch (stage->sp_func) {
	case IA_CSS_PIPELINE_RAW_COPY:
		sh_css_sp_start_raw_copy(args->out_frame[0],
					 pipe_num, two_ppc,
					 stage->max_input_width,
					 copy_ovrd, if_config_index);
		break;
	case IA_CSS_PIPELINE_BIN_COPY:
		assert(false); /* TBI */
		break;
	case IA_CSS_PIPELINE_ISYS_COPY:
		sh_css_sp_start_isys_copy(args->out_frame[0],
					  pipe_num, stage->max_input_width, if_config_index);
		break;
	case IA_CSS_PIPELINE_NO_FUNC:
		assert(false);
		break;
	}
}

void
sh_css_sp_init_pipeline(struct ia_css_pipeline *me,
			enum ia_css_pipe_id id,
			u8 pipe_num,
			bool xnr,
			bool two_ppc,
			bool continuous,
			bool offline,
			unsigned int required_bds_factor,
			enum sh_css_pipe_config_override copy_ovrd,
			enum ia_css_input_mode input_mode,
			const struct ia_css_metadata_config *md_config,
			const struct ia_css_metadata_info *md_info,
			const enum mipi_port_id port_id)
{
	/* Get first stage */
	struct ia_css_pipeline_stage *stage        = NULL;
	struct ia_css_binary	     *first_binary = NULL;
	struct ia_css_pipe *pipe = NULL;
	unsigned int num;
	enum ia_css_pipe_id pipe_id = id;
	unsigned int thread_id;
	u8 if_config_index, tmp_if_config_index;

	if (!me->stages) {
		dev_err(atomisp_dev, "%s called on a pipeline without stages\n",
			__func__);
		return; /* FIXME should be able to return an error */
	}

	first_binary = me->stages->binary;

	if (input_mode == IA_CSS_INPUT_MODE_SENSOR ||
	    input_mode == IA_CSS_INPUT_MODE_BUFFERED_SENSOR) {
		assert(port_id < N_MIPI_PORT_ID);
		if (port_id >= N_MIPI_PORT_ID) /* should not happen but KW does not know */
			return; /* we should be able to return an error */
		if_config_index  = (uint8_t)(port_id - MIPI_PORT0_ID);
	} else if (input_mode == IA_CSS_INPUT_MODE_MEMORY) {
		if_config_index = SH_CSS_IF_CONFIG_NOT_NEEDED;
	} else {
		if_config_index = 0x0;
	}

	ia_css_pipeline_get_sp_thread_id(pipe_num, &thread_id);
	memset(&sh_css_sp_group.pipe[thread_id], 0, sizeof(struct sh_css_sp_pipeline));

	/* Count stages */
	for (stage = me->stages, num = 0; stage; stage = stage->next, num++) {
		stage->stage_num = num;
		ia_css_debug_pipe_graph_dump_stage(stage, id);
	}
	me->num_stages = num;

	if (first_binary) {
		/* Init pipeline data */
		sh_css_sp_init_group(two_ppc, first_binary->input_format,
				     offline, if_config_index);
	} /* if (first_binary != NULL) */

	/* Signal the host immediately after start for SP_ISYS_COPY only */
	if (me->num_stages == 1 &&
	    me->stages->sp_func == IA_CSS_PIPELINE_ISYS_COPY)
		sh_css_sp_group.config.no_isp_sync = true;

	/* Init stage data */
	sh_css_init_host2sp_frame_data();

	sh_css_sp_group.pipe[thread_id].num_stages = 0;
	sh_css_sp_group.pipe[thread_id].pipe_id = pipe_id;
	sh_css_sp_group.pipe[thread_id].thread_id = thread_id;
	sh_css_sp_group.pipe[thread_id].pipe_num = pipe_num;
	sh_css_sp_group.pipe[thread_id].num_execs = me->num_execs;
	sh_css_sp_group.pipe[thread_id].pipe_qos_config = QOS_INVALID;
	sh_css_sp_group.pipe[thread_id].required_bds_factor = required_bds_factor;
	sh_css_sp_group.pipe[thread_id].input_system_mode
	= (uint32_t)input_mode;
	sh_css_sp_group.pipe[thread_id].port_id = port_id;
	sh_css_sp_group.pipe[thread_id].dvs_frame_delay = (uint32_t)me->dvs_frame_delay;

	/* TODO: next indicates from which queues parameters need to be
		 sampled, needs checking/improvement */
	if (ia_css_pipeline_uses_params(me)) {
		sh_css_sp_group.pipe[thread_id].pipe_config =
		SH_CSS_PIPE_CONFIG_SAMPLE_PARAMS << thread_id;
	}

	/*
	 * For continuous use-cases, SP copy is responsible for sampling the
	 * parameters
	 */
	if (continuous)
		sh_css_sp_group.pipe[thread_id].pipe_config = 0;

	sh_css_sp_group.pipe[thread_id].inout_port_config = me->inout_port_config;

	pipe = find_pipe_by_num(pipe_num);
	assert(pipe);
	if (!pipe)
		return;

	sh_css_sp_group.pipe[thread_id].scaler_pp_lut = sh_css_pipe_get_pp_gdc_lut(pipe);

	if (md_info && md_info->size > 0) {
		sh_css_sp_group.pipe[thread_id].metadata.width  = md_info->resolution.width;
		sh_css_sp_group.pipe[thread_id].metadata.height = md_info->resolution.height;
		sh_css_sp_group.pipe[thread_id].metadata.stride = md_info->stride;
		sh_css_sp_group.pipe[thread_id].metadata.size   = md_info->size;
		ia_css_isys_convert_stream_format_to_mipi_format(
		    md_config->data_type, MIPI_PREDICTOR_NONE,
		    &sh_css_sp_group.pipe[thread_id].metadata.format);
	}

	sh_css_sp_group.pipe[thread_id].output_frame_queue_id = (uint32_t)SH_CSS_INVALID_QUEUE_ID;
	if (pipe_id != IA_CSS_PIPE_ID_COPY) {
		ia_css_query_internal_queue_id(IA_CSS_BUFFER_TYPE_OUTPUT_FRAME, thread_id,
					       (enum sh_css_queue_id *)(
						   &sh_css_sp_group.pipe[thread_id].output_frame_queue_id));
	}

	IA_CSS_LOG("pipe_id %d port_config %08x",
		   pipe_id, sh_css_sp_group.pipe[thread_id].inout_port_config);

	for (stage = me->stages, num = 0; stage; stage = stage->next, num++) {
		sh_css_sp_group.pipe[thread_id].num_stages++;
		if (is_sp_stage(stage)) {
			sp_init_sp_stage(stage, pipe_num, two_ppc,
					 copy_ovrd, if_config_index);
		} else {
			if ((stage->stage_num != 0) ||
			    SH_CSS_PIPE_PORT_CONFIG_IS_CONTINUOUS(me->inout_port_config))
				tmp_if_config_index = SH_CSS_IF_CONFIG_NOT_NEEDED;
			else
				tmp_if_config_index = if_config_index;
			sp_init_stage(stage, pipe_num,
				      xnr, tmp_if_config_index, two_ppc);
		}

		store_sp_stage_data(pipe_id, pipe_num, num);
	}
	sh_css_sp_group.pipe[thread_id].pipe_config |= (uint32_t)
		(me->acquire_isp_each_stage << IA_CSS_ACQUIRE_ISP_POS);
	store_sp_group_data();
}

void
sh_css_sp_uninit_pipeline(unsigned int pipe_num)
{
	unsigned int thread_id;

	ia_css_pipeline_get_sp_thread_id(pipe_num, &thread_id);
	/*memset(&sh_css_sp_group.pipe[thread_id], 0, sizeof(struct sh_css_sp_pipeline));*/
	sh_css_sp_group.pipe[thread_id].num_stages = 0;
}

bool sh_css_write_host2sp_command(enum host2sp_commands host2sp_command)
{
	unsigned int HIVE_ADDR_host_sp_com = sh_css_sp_fw.info.sp.host_sp_com;
	unsigned int offset = (unsigned int)offsetof(struct host_sp_communication,
			      host2sp_command)
			      / sizeof(int);
	enum host2sp_commands last_cmd = host2sp_cmd_error;
	(void)HIVE_ADDR_host_sp_com; /* Suppress warnings in CRUN */

	/* Previous command must be handled by SP (by design) */
	last_cmd = load_sp_array_uint(host_sp_com, offset);
	if (last_cmd != host2sp_cmd_ready)
		IA_CSS_ERROR("last host command not handled by SP(%d)", last_cmd);

	store_sp_array_uint(host_sp_com, offset, host2sp_command);

	return (last_cmd == host2sp_cmd_ready);
}

enum host2sp_commands
sh_css_read_host2sp_command(void)
{
	unsigned int HIVE_ADDR_host_sp_com = sh_css_sp_fw.info.sp.host_sp_com;
	unsigned int offset = (unsigned int)offsetof(struct host_sp_communication, host2sp_command)
	/ sizeof(int);
	(void)HIVE_ADDR_host_sp_com; /* Suppress warnings in CRUN */
	return (enum host2sp_commands)load_sp_array_uint(host_sp_com, offset);
}

/*
 * Frame data is no longer part of the sp_stage structure but part of a
 * separate structure. The aim is to make the sp_data struct static
 * (it defines a pipeline) and that the dynamic (per frame) data is stored
 * separately.
 *
 * This function must be called first every where were you start constructing
 * a new pipeline by defining one or more stages with use of variable
 * sh_css_sp_stage. Even the special cases like accelerator and copy_frame
 * These have a pipeline of just 1 stage.
 */
void
sh_css_init_host2sp_frame_data(void)
{
	/* Clean table */
	unsigned int HIVE_ADDR_host_sp_com = sh_css_sp_fw.info.sp.host_sp_com;

	(void)HIVE_ADDR_host_sp_com; /* Suppress warnings in CRUN */
	/*
	 * rvanimme: don't clean it to save static frame info line ref_in
	 * ref_out, and tnr_frames. Once this static data is in a
	 * separate data struct, this may be enable (but still, there is
	 * no need for it)
	 */
}

/*
 * @brief Update the offline frame information in host_sp_communication.
 * Refer to "sh_css_sp.h" for more details.
 */
void
sh_css_update_host2sp_offline_frame(
    unsigned int frame_num,
    struct ia_css_frame *frame,
    struct ia_css_metadata *metadata)
{
	unsigned int HIVE_ADDR_host_sp_com;
	unsigned int offset;

	assert(frame_num < NUM_CONTINUOUS_FRAMES);

	/* Write new frame data into SP DMEM */
	HIVE_ADDR_host_sp_com = sh_css_sp_fw.info.sp.host_sp_com;
	offset = (unsigned int)offsetof(struct host_sp_communication,
					host2sp_offline_frames)
		 / sizeof(int);
	offset += frame_num;
	store_sp_array_uint(host_sp_com, offset, frame ? frame->data : 0);

	/* Write metadata buffer into SP DMEM */
	offset = (unsigned int)offsetof(struct host_sp_communication,
					host2sp_offline_metadata)
		 / sizeof(int);
	offset += frame_num;
	store_sp_array_uint(host_sp_com, offset, metadata ? metadata->address : 0);
}

/*
 * @brief Update the mipi frame information in host_sp_communication.
 * Refer to "sh_css_sp.h" for more details.
 */
void
sh_css_update_host2sp_mipi_frame(
    unsigned int frame_num,
    struct ia_css_frame *frame)
{
	unsigned int HIVE_ADDR_host_sp_com;
	unsigned int offset;

	/* MIPI buffers are dedicated to port, so now there are more of them. */
	assert(frame_num < (N_CSI_PORTS * NUM_MIPI_FRAMES_PER_STREAM));

	/* Write new frame data into SP DMEM */
	HIVE_ADDR_host_sp_com = sh_css_sp_fw.info.sp.host_sp_com;
	offset = (unsigned int)offsetof(struct host_sp_communication,
					host2sp_mipi_frames)
		 / sizeof(int);
	offset += frame_num;

	store_sp_array_uint(host_sp_com, offset,
			    frame ? frame->data : 0);
}

/*
 * @brief Update the mipi metadata information in host_sp_communication.
 * Refer to "sh_css_sp.h" for more details.
 */
void
sh_css_update_host2sp_mipi_metadata(
    unsigned int frame_num,
    struct ia_css_metadata *metadata)
{
	unsigned int HIVE_ADDR_host_sp_com;
	unsigned int o;

	/* MIPI buffers are dedicated to port, so now there are more of them. */
	assert(frame_num < (N_CSI_PORTS * NUM_MIPI_FRAMES_PER_STREAM));

	/* Write new frame data into SP DMEM */
	HIVE_ADDR_host_sp_com = sh_css_sp_fw.info.sp.host_sp_com;
	o = offsetof(struct host_sp_communication, host2sp_mipi_metadata)
	    / sizeof(int);
	o += frame_num;
	store_sp_array_uint(host_sp_com, o,
			    metadata ? metadata->address : 0);
}

void
sh_css_update_host2sp_num_mipi_frames(unsigned int num_frames)
{
	unsigned int HIVE_ADDR_host_sp_com;
	unsigned int offset;

	/* Write new frame data into SP DMEM */
	HIVE_ADDR_host_sp_com = sh_css_sp_fw.info.sp.host_sp_com;
	offset = (unsigned int)offsetof(struct host_sp_communication,
					host2sp_num_mipi_frames)
		 / sizeof(int);

	store_sp_array_uint(host_sp_com, offset, num_frames);
}

void
sh_css_update_host2sp_cont_num_raw_frames(unsigned int num_frames,
	bool set_avail)
{
	const struct ia_css_fw_info *fw;
	unsigned int HIVE_ADDR_host_sp_com;
	unsigned int extra_num_frames, avail_num_frames;
	unsigned int offset, offset_extra;

	/* Write new frame data into SP DMEM */
	fw = &sh_css_sp_fw;
	HIVE_ADDR_host_sp_com = fw->info.sp.host_sp_com;
	if (set_avail) {
		offset = (unsigned int)offsetof(struct host_sp_communication,
						host2sp_cont_avail_num_raw_frames)
			 / sizeof(int);
		avail_num_frames = load_sp_array_uint(host_sp_com, offset);
		extra_num_frames = num_frames - avail_num_frames;
		offset_extra = (unsigned int)offsetof(struct host_sp_communication,
						      host2sp_cont_extra_num_raw_frames)
			       / sizeof(int);
		store_sp_array_uint(host_sp_com, offset_extra, extra_num_frames);
	} else
		offset = (unsigned int)offsetof(struct host_sp_communication,
						host2sp_cont_target_num_raw_frames)
			 / sizeof(int);

	store_sp_array_uint(host_sp_com, offset, num_frames);
}

void
sh_css_event_init_irq_mask(void)
{
	int i;
	unsigned int HIVE_ADDR_host_sp_com = sh_css_sp_fw.info.sp.host_sp_com;
	unsigned int offset;
	struct sh_css_event_irq_mask event_irq_mask_init;

	event_irq_mask_init.or_mask  = IA_CSS_EVENT_TYPE_ALL;
	event_irq_mask_init.and_mask = IA_CSS_EVENT_TYPE_NONE;
	(void)HIVE_ADDR_host_sp_com; /* Suppress warnings in CRUN */

	assert(sizeof(event_irq_mask_init) % HRT_BUS_BYTES == 0);
	for (i = 0; i < IA_CSS_PIPE_ID_NUM; i++) {
		offset = (unsigned int)offsetof(struct host_sp_communication,
						host2sp_event_irq_mask[i]);
		assert(offset % HRT_BUS_BYTES == 0);
		sp_dmem_store(SP0_ID,
			      (unsigned int)sp_address_of(host_sp_com) + offset,
			      &event_irq_mask_init, sizeof(event_irq_mask_init));
	}
}

int
ia_css_pipe_set_irq_mask(struct ia_css_pipe *pipe,
			 unsigned int or_mask,
			 unsigned int and_mask)
{
	unsigned int HIVE_ADDR_host_sp_com = sh_css_sp_fw.info.sp.host_sp_com;
	unsigned int offset;
	struct sh_css_event_irq_mask event_irq_mask;
	unsigned int pipe_num;

	assert(pipe);

	assert(IA_CSS_PIPE_ID_NUM == NR_OF_PIPELINES);
	/*
	 * Linux kernel does not have UINT16_MAX
	 * Therefore decided to comment out these 2 asserts for Linux
	 * Alternatives that were not chosen:
	 * - add a conditional #define for UINT16_MAX
	 * - compare with (uint16_t)~0 or 0xffff
	 * - different assert for Linux and Windows
	 */

	(void)HIVE_ADDR_host_sp_com; /* Suppress warnings in CRUN */

	IA_CSS_LOG("or_mask=%x, and_mask=%x", or_mask, and_mask);
	event_irq_mask.or_mask  = (uint16_t)or_mask;
	event_irq_mask.and_mask = (uint16_t)and_mask;

	pipe_num = ia_css_pipe_get_pipe_num(pipe);
	if (pipe_num >= IA_CSS_PIPE_ID_NUM)
		return -EINVAL;
	offset = (unsigned int)offsetof(struct host_sp_communication,
					host2sp_event_irq_mask[pipe_num]);
	assert(offset % HRT_BUS_BYTES == 0);
	sp_dmem_store(SP0_ID,
		      (unsigned int)sp_address_of(host_sp_com) + offset,
		      &event_irq_mask, sizeof(event_irq_mask));

	return 0;
}

int
ia_css_event_get_irq_mask(const struct ia_css_pipe *pipe,
			  unsigned int *or_mask,
			  unsigned int *and_mask)
{
	unsigned int HIVE_ADDR_host_sp_com = sh_css_sp_fw.info.sp.host_sp_com;
	unsigned int offset;
	struct sh_css_event_irq_mask event_irq_mask;
	unsigned int pipe_num;

	(void)HIVE_ADDR_host_sp_com; /* Suppress warnings in CRUN */

	IA_CSS_ENTER_LEAVE("");

	assert(pipe);
	assert(IA_CSS_PIPE_ID_NUM == NR_OF_PIPELINES);

	pipe_num = ia_css_pipe_get_pipe_num(pipe);
	if (pipe_num >= IA_CSS_PIPE_ID_NUM)
		return -EINVAL;
	offset = (unsigned int)offsetof(struct host_sp_communication,
					host2sp_event_irq_mask[pipe_num]);
	assert(offset % HRT_BUS_BYTES == 0);
	sp_dmem_load(SP0_ID,
		     (unsigned int)sp_address_of(host_sp_com) + offset,
		     &event_irq_mask, sizeof(event_irq_mask));

	if (or_mask)
		*or_mask = event_irq_mask.or_mask;

	if (and_mask)
		*and_mask = event_irq_mask.and_mask;

	return 0;
}

void
sh_css_sp_set_sp_running(bool flag)
{
	sp_running = flag;
}

bool
sh_css_sp_is_running(void)
{
	return sp_running;
}

void
sh_css_sp_start_isp(void)
{
	const struct ia_css_fw_info *fw;
	unsigned int HIVE_ADDR_sp_sw_state;

	fw = &sh_css_sp_fw;
	HIVE_ADDR_sp_sw_state = fw->info.sp.sw_state;

	if (sp_running)
		return;

	(void)HIVE_ADDR_sp_sw_state; /* Suppress warnings in CRUN */

	/* no longer here, sp started immediately */
	/*ia_css_debug_pipe_graph_dump_epilogue();*/

	store_sp_group_data();
	store_sp_per_frame_data(fw);

	sp_dmem_store_uint32(SP0_ID,
			     (unsigned int)sp_address_of(sp_sw_state),
			     (uint32_t)(IA_CSS_SP_SW_TERMINATED));

	/*
	 * Note 1: The sp_start_isp function contains a wait till
	 * the input network is configured by the SP.
	 * Note 2: Not all SP binaries supports host2sp_commands.
	 * In case a binary does support it, the host2sp_command
	 * will have status cmd_ready after return of the function
	 * sh_css_hrt_sp_start_isp. There is no race-condition here
	 * because only after the process_frame command has been
	 * received, the SP starts configuring the input network.
	 */

	/*
	 * We need to set sp_running before we call ia_css_mmu_invalidate_cache
	 * as ia_css_mmu_invalidate_cache checks on sp_running to
	 * avoid that it accesses dmem while the SP is not powered
	 */
	sp_running = true;
	ia_css_mmu_invalidate_cache();
	/* Invalidate all MMU caches */
	mmu_invalidate_cache_all();

	ia_css_spctrl_start(SP0_ID);
}

bool
ia_css_isp_has_started(void)
{
	const struct ia_css_fw_info *fw = &sh_css_sp_fw;
	unsigned int HIVE_ADDR_ia_css_ispctrl_sp_isp_started = fw->info.sp.isp_started;
	(void)HIVE_ADDR_ia_css_ispctrl_sp_isp_started; /* Suppress warnings in CRUN */

	return (bool)load_sp_uint(ia_css_ispctrl_sp_isp_started);
}

/*
 * @brief Initialize the DMA software-mask in the debug mode.
 * Refer to "sh_css_sp.h" for more details.
 */
bool
sh_css_sp_init_dma_sw_reg(int dma_id)
{
	int i;

	/* enable all the DMA channels */
	for (i = 0; i < N_DMA_CHANNEL_ID; i++) {
		/* enable the writing request */
		sh_css_sp_set_dma_sw_reg(dma_id,
					 i,
					 0,
					 true);
		/* enable the reading request */
		sh_css_sp_set_dma_sw_reg(dma_id,
					 i,
					 1,
					 true);
	}

	return true;
}

/*
 * @brief Set the DMA software-mask in the debug mode.
 * Refer to "sh_css_sp.h" for more details.
 */
bool
sh_css_sp_set_dma_sw_reg(int dma_id,
			 int channel_id,
			 int request_type,
			 bool enable)
{
	u32 sw_reg;
	u32 bit_val;
	u32 bit_offset;
	u32 bit_mask;

	(void)dma_id;

	assert(channel_id >= 0 && channel_id < N_DMA_CHANNEL_ID);
	assert(request_type >= 0);

	/* get the software-mask */
	sw_reg =
	    sh_css_sp_group.debug.dma_sw_reg;

	/* get the offset of the target bit */
	bit_offset = (8 * request_type) + channel_id;

	/* clear the value of the target bit */
	bit_mask = ~(1 << bit_offset);
	sw_reg &= bit_mask;

	/* set the value of the bit for the DMA channel */
	bit_val = enable ? 1 : 0;
	bit_val <<= bit_offset;
	sw_reg |= bit_val;

	/* update the software status of DMA channels */
	sh_css_sp_group.debug.dma_sw_reg = sw_reg;

	return true;
}

void
sh_css_sp_reset_global_vars(void)
{
	memset(&sh_css_sp_group, 0, sizeof(struct sh_css_sp_group));
	memset(&sh_css_sp_stage, 0, sizeof(struct sh_css_sp_stage));
	memset(&sh_css_isp_stage, 0, sizeof(struct sh_css_isp_stage));
	memset(&sh_css_sp_output, 0, sizeof(struct sh_css_sp_output));
	memset(&per_frame_data, 0, sizeof(struct sh_css_sp_per_frame_data));
}
